﻿// --------------------------------------------------------------------------------------------------------------------
// <summary>
//   CheckLogFileStep.cs
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace TRlib.BizUnit.CustomSteps
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Xml;
    using global::BizUnit;

    /// <summary>
    /// The CheckLogFileStep checks a log file for one or more entries based on newline separator
    /// </summary>
    /// 
    /// <remarks>
    /// The following shows an example of the Xml representation of this test step.
    /// 
    /// <code escaped="true">
    ///    <TestStep assemblyPath="" typeName="CheckLogFileStep">
    ///        <Retry>
    ///           <RetryAttempts>30</RetryAttempts>
    ///           <RetryIntervalMs>1000</RetryIntervalMs>
    ///        </Retry>
    ///        <LogFilePath>C:\MyLogs\Log.txt</LogFilePath>
    ///        <MatchPatterns>
    ///           <MatchPattern>SomeRegexExpression</MatchPattern>
    ///           <MatchPattern>SomeRegexExpression</MatchPattern>
    ///           <MatchPattern>SomeRegexExpression</MatchPattern>
    ///        </MatchPatterns>
    ///    </TestStep>
    /// </code>
    ///    
    ///    <list type="table">
    ///        <listheader>
    ///            <term>Tag</term>
    ///            <description>Description</description>
    ///        </listheader>
    ///        <item>
    ///            <term>Retry/RetryAttempts</term>
    ///            <description>Number of retry attempts allowed</description>
    ///        </item>
    ///        <item>
    ///            <term>Retry/RetryIntervalMs</term>
    ///            <description>Interval of retries in miliseconds.</description>
    ///        </item>
    ///        <item>
    ///            <term>LogFilePath</term>
    ///            <description>Path to file</description>
    ///        </item>
    ///        <item>
    ///            <term>MatchPatterns/MatchPattern</term>
    ///            <description>The regex expression to check the log entry line against. Can define as many as there are lines in the log</description>
    ///        </item>
    ///    </list>
    /// </remarks>
    public class CheckLogFileStep : ITestStep
    {
        #region ITestStep Members
        public void Execute(System.Xml.XmlNode testConfig, Context context)
        {
            // Read config
            string logPath = context.ReadConfigAsString(testConfig, "LogFilePath");
            XmlNode matchPatterns = testConfig.SelectSingleNode("MatchPatterns");

            // Retry
            int retryAttempts = 0;
            int retryDelay = 0;
            if (testConfig.SelectSingleNode("Retry") != null)
            {
                retryAttempts = context.ReadConfigAsInt32(testConfig, "Retry/RetryAttempts");
                retryDelay = context.ReadConfigAsInt32(testConfig, "Retry/RetryIntervalMs");
            }

            // Set retry flag on if retry attempts are > 0. 
            bool retry = retryAttempts > 0;
            int tryCount = 0;

            // tryCount always zero on first run
            while (retry || tryCount == 0)
            {
                // Increment try-count
                tryCount++;

                // Initialise found to true
                bool found = true;

                // Retry scope - inside here any exceptions will be silent as long as there are retries pending.
                try
                {
                    // Fix the count of events at this point in time
                    int logEntryPosition = 0;

                    string matchPattern = String.Empty;
                    string entry = String.Empty;

                    // Get the log in a non-locking manner
                    using (FileStream fs = new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    {
                        StreamReader sr = new StreamReader(fs);

                        while (!sr.EndOfStream)
                        {
                            // Get log entry
                            entry = sr.ReadLine();

                            // Get the equivalent regex from the config
                            matchPattern = matchPatterns.ChildNodes[logEntryPosition].FirstChild.Value;

                            // Perform match
                            Match match = Regex.Match(entry, matchPattern);
                            if (match.Success)
                            {
                                context.LogInfo(String.Format("Found entry matching expression '{0}' at position '{1}' in log.", matchPattern, logEntryPosition));
                            }
                            else
                            {
                                found = false;
                                break;
                            }

                            logEntryPosition++;   
                        }

                        // Check totals 
                        if (!(matchPatterns.ChildNodes.Count == logEntryPosition))
                        {
                            throw new CustomBizUnitExecutionException(String.Format("Number of entries in the log {0} does not match number of expected entries {1}", logEntryPosition, matchPatterns.ChildNodes.Count));
                        }

                        // If no entries found
                        if (logEntryPosition == 0)
                        {
                            throw new CustomBizUnitExecutionException("Log file is empty");
                        }
                    }

                    if (!found)
                    {
                        throw new CustomBizUnitExecutionException(String.Format("Could not find match for '{0}' against entry '{1}' at position {2} in the log.", matchPattern, entry, logEntryPosition));
                    }

                    // If no exception thrown by this point then has been successful. So set retry = false
                    retry = false;
                    context.LogInfo(String.Format("Found entry matching expression '{0}' at position '{1}' in log.", matchPattern, logEntryPosition));
                }
                catch (Exception)
                {
                    // Work out whether to suppress exception for another retry...
                    if (tryCount >= retryAttempts)
                    {
                        // If try-count >= retry attempts - throw to caller
                        throw;
                    }

                    // Otherwise supress exception and delay before continuing
                    Thread.Sleep(retryDelay);
                    context.LogInfo(String.Format("Retry attempt {0} of {1}", tryCount, retryAttempts));
                } 
            } // while retry or trycount = 0
        }
        #endregion
    }
}
